/******************************************************************************\
 * Filename   : enc_stream.c
 * Copyright  : 
 * Created    : 2018-11-08 by litao
 * Description:
 * 
\******************************************************************************/

#include "enc_stream.h"
#include "enc_output.h"
#include <sys/mman.h>


int fd_yuv = -1;
int fd_file = -1;
static HI_S32 s_s32MemDev = -1;

#define MEM_DEV_OPEN() \
do {\
    	if (s_s32MemDev <= 0)\
        {\
            s_s32MemDev = open("/dev/mem", O_CREAT|O_RDWR|O_SYNC);\
            if (s_s32MemDev < 0)\
            {\
                perror("Open dev/mem error");\
                return -1;\
            }\
        }\
}while(0)

#define MEM_DEV_CLOSE() \
do {\
        HI_S32 s32Ret;\
    	if (s_s32MemDev > 0)\
        {\
            s32Ret = close(s_s32MemDev);\
            if(HI_SUCCESS != s32Ret)\
            {\
                perror("Close mem/dev Fail");\
                return s32Ret;\
            }\
            s_s32MemDev = -1;\
        }\
}while(0)

HI_VOID * COMM_SYS_Mmap(HI_U32 u32PhyAddr, HI_U32 u32Size)
{
    HI_U32 u32Diff;
    HI_U32 u32PagePhy;
    HI_U32 u32PageSize;
    HI_U8 * pPageAddr;   

    /* The mmap address should align with page */
    u32PagePhy = u32PhyAddr & 0xfffff000;   
    u32Diff    = u32PhyAddr - u32PagePhy;

    /* The mmap size shuld be mutliples of 1024 */
    u32PageSize = ((u32Size + u32Diff - 1) & 0xfffff000) + 0x1000;  
    pPageAddr   = (HI_U8 *)mmap ((void *)0, u32PageSize, PROT_READ|PROT_WRITE,
                                    MAP_SHARED, s_s32MemDev, u32PagePhy);   
    if (MAP_FAILED == pPageAddr )   
    {    
        perror("mmap error");
        return NULL;    
    }
    return (HI_VOID *) (pPageAddr + u32Diff);
}

HI_S32 COMM_SYS_Munmap(HI_VOID* pVirAddr, HI_U32 u32Size)
{
    HI_U32 u32PageAddr;
    HI_U32 u32PageSize;
    HI_U32 u32Diff;

    u32PageAddr = (((HI_U32)pVirAddr) & 0xfffff000);
    u32Diff     = (HI_U32)pVirAddr - u32PageAddr;
    u32PageSize = ((u32Size + u32Diff - 1) & 0xfffff000) + 0x1000;

    return munmap((HI_VOID*)u32PageAddr, u32PageSize);
}


int enc_video_stream(int venc_chan, stENC_CHLSET *p_chanSet)
{
	int ret;
	uint32_t i;
	uint8_t *p_dataAddr;
	uint8_t *p_currAddr;
	uint32_t data_len;
	uint32_t total_len;
	VENC_STREAM_S stream_packet;
	VENC_CHN_STAT_S chan_stat;
	carrier_buffer_t *p_carrier;
	VENC_PACK_S *p_vpack;
	enc_av_data_head video_data_head;

	//save raw YUV
#if 0
	static int cap_count = 10;
	int s32Ret;
	VIDEO_FRAME_INFO_S get_frame;
	char *p_addr = NULL;
	HI_CHAR *p_user_addr[2];
	char *p_virt_y;
	char *p_virt_c;
	char *p_mem;

	if (fd_yuv < 0) {
		fd_yuv = open("/tmp/raw.yuv", O_WRONLY | O_CREAT);
		if (fd_yuv < 0) {
			printf("open raw yuv error %d\n", fd_yuv);
			return HI_FAILURE;			
		}		
	}

	if (cap_count--) {

		MEM_DEV_OPEN();

		s32Ret = HI_MPI_VI_SetFrameDepth(venc_chan*8, 3);
		if (HI_SUCCESS != s32Ret)
		{
			enc_err("HI_MPI_VI_SetFrameDepth chn[%d] failed with %#x!\n", venc_chan*8, s32Ret);
			return HI_FAILURE;
		}

		s32Ret = HI_MPI_VI_GetFrame(venc_chan*8, &get_frame, -1);
		if (HI_SUCCESS != s32Ret)
		{
			enc_err("HI_MPI_VI_GetFrame chn[%d] failed with %#x!\n", venc_chan*8, s32Ret);
			return HI_FAILURE;
		}	

		p_user_addr[0] = (HI_CHAR *)COMM_SYS_Mmap(get_frame.stVFrame.u32PhyAddr[0], 
			get_frame.stVFrame.u32Stride[0] * 1080 * 3 /2);

		p_virt_y = p_user_addr[0];
		p_virt_c = p_virt_y + get_frame.stVFrame.u32Stride[0] * 1080;

		

		printf("get frame width %d height %d pix_fmt %d\n", get_frame.stVFrame.u32Width,
		get_frame.stVFrame.u32Height, get_frame.stVFrame.enPixelFormat);
		p_addr = p_virt_y;
		printf("Y[%x][%x][%x][%x]\n", p_addr[0], p_addr[1], p_addr[2], p_addr[3]);
		p_addr = p_virt_c;
		printf("UV[%x][%x][%x][%x]\n", p_addr[0], p_addr[1], p_addr[2], p_addr[3]);

		
		write(fd_yuv, p_virt_y, 1920*1080);
		write(fd_yuv, p_virt_c, 1920*1080/2);
		COMM_SYS_Munmap(p_user_addr[0], get_frame.stVFrame.u32Stride[0] * 1080 * 3 /2);
		HI_MPI_VI_ReleaseFrame(venc_chan*8, &get_frame);
		MEM_DEV_CLOSE();
	}
#endif	

    /*******************************************************
        step 1 : query how many packs in one-frame stream.
    *******************************************************/
    memset(&stream_packet, 0, sizeof(stream_packet));
    ret = HI_MPI_VENC_Query(venc_chan, &chan_stat);
    if (HI_SUCCESS != ret)
    {
        enc_err("HI_MPI_VENC_Query chn[%d] failed with %#x!\n", venc_chan, ret);
        return HI_FAILURE;
    }

    /*******************************************************
        step 2 : malloc corresponding number of pack nodes.
    *******************************************************/
    stream_packet.pstPack = (VENC_PACK_S *)malloc(sizeof(VENC_PACK_S) * chan_stat.u32CurPacks);
    if (NULL == stream_packet.pstPack)
    {
        enc_err("malloc stream pack failed!\n");
        return HI_FAILURE;
    }

    /*******************************************************
        step 3 : call mpi to get one-frame stream
    *******************************************************/
    stream_packet.u32PackCount = chan_stat.u32CurPacks;
    ret = HI_MPI_VENC_GetStream(venc_chan, &stream_packet, HI_TRUE);
    if (HI_SUCCESS != ret)
    {
        free(stream_packet.pstPack);
        stream_packet.pstPack = NULL;
        enc_err("HI_MPI_VENC_GetStream failed with %#x!\n", ret);
        return HI_FAILURE;
    }

    /*******************************************************
        step 4 : save frame to file
    *******************************************************/
    total_len = 0;
	for (i=0; i<stream_packet.u32PackCount; i++) {
		p_vpack = &stream_packet.pstPack[i];
		data_len = p_vpack->u32Len - p_vpack->u32Offset;
		total_len += data_len;
	}	

	p_carrier = alloc_carrier_buffer(total_len + sizeof(enc_av_data_head));
	if (p_carrier == NULL) {
		HI_MPI_VENC_ReleaseStream(venc_chan, &stream_packet);
		return -1;
	}
	
	p_currAddr = p_carrier->data + sizeof(enc_av_data_head);

	total_len = 0;
	for (i=0; i<stream_packet.u32PackCount; i++) {
		p_vpack = &stream_packet.pstPack[i];

		p_dataAddr = p_vpack->pu8Addr + p_vpack->u32Offset;
		data_len = p_vpack->u32Len - p_vpack->u32Offset;

		video_data_head.pts = p_vpack->u64PTS/100*9;
		video_data_head.dts = p_vpack->u64PTS/100*9;

		if ((p_vpack->DataType.enH264EType == H264E_NALU_ISLICE) || (p_vpack->DataType.enH264EType == H264E_NALU_IDRSLICE) || 
			(p_vpack->DataType.enH265EType == H265E_NALU_ISLICE) || (p_vpack->DataType.enH265EType == H265E_NALU_IDRSLICE)) {
			video_data_head.key_frame = 1;
		} else {
			video_data_head.key_frame = 0;
		}

		memcpy(p_currAddr+total_len, p_dataAddr, data_len);
		total_len += data_len;
	}

	/*if (fd_file < 0) {
		fd_file = open("/mnt/raw.h264", O_RDWR | O_CREAT);
	}
	write(fd_file, p_currAddr, total_len);*/

	if (main_data.is_firstframe == 0)
	{
		main_data.first_pts_shift = video_data_head.pts;
		main_data.is_firstframe = 1;
	}

	video_data_head.pts -= main_data.first_pts_shift;
	video_data_head.dts -= main_data.first_pts_shift;

	video_data_head.data_type = ENC_RAW_VIDEO;
	video_data_head.codec_id = p_chanSet->encchl_vid.vid_codec == 1 ? AV_CODEC_ID_H264 : AV_CODEC_ID_HEVC;
	video_data_head.width = p_chanSet->encchl_vid.vid_width;
	video_data_head.height = p_chanSet->encchl_vid.vid_height;
	video_data_head.timebase_num = 1;
	video_data_head.timebase_den = 90000;

	p_carrier->data_len = total_len + sizeof(enc_av_data_head);
	p_carrier->data_offset = 0;
	memcpy(p_carrier->data, &video_data_head, sizeof(enc_av_data_head));

	qlist_push(&main_data.avout_queue, p_carrier);

    /*******************************************************
        step 5 : release stream
    *******************************************************/
    ret = HI_MPI_VENC_ReleaseStream(venc_chan, &stream_packet);
    if (HI_SUCCESS != ret)
    {
        free(stream_packet.pstPack);
        stream_packet.pstPack = NULL;
        return HI_FAILURE;
    }
    /*******************************************************
        step 6 : free pack nodes
    *******************************************************/
    free(stream_packet.pstPack);
    stream_packet.pstPack = NULL;

	return HI_SUCCESS;	
}

int enc_audio_stream(int aenc_chan, stENC_CHLSET *p_chanSet)
{
	int ret;
	carrier_buffer_t *p_carrier;
	AUDIO_STREAM_S stream_packet;
	enc_av_data_head audio_data_head;

	ret = HI_MPI_AENC_GetStream(aenc_chan, &stream_packet, HI_FALSE);
	if (HI_SUCCESS != ret)
	{
		enc_err("HI_MPI_AENC_GetStream(%d), failed with %#x!\n", aenc_chan, ret);
		return ret;
	}

	audio_data_head.data_type = ENC_RAW_AUDIO;
	audio_data_head.codec_id = p_chanSet->encchl_aud.aud_codec;
	audio_data_head.sample_rate = p_chanSet->encchl_aud.aud_smp_rate;
	audio_data_head.timebase_num = 1;
	audio_data_head.timebase_den = 90000;
	audio_data_head.pts = stream_packet.u64TimeStamp/100*9;
	audio_data_head.dts = stream_packet.u64TimeStamp/100*9;
	audio_data_head.key_frame = 1;
	
	p_carrier = alloc_carrier_buffer(stream_packet.u32Len + sizeof(enc_av_data_head));
	if (p_carrier == NULL) {
		HI_MPI_AENC_ReleaseStream(aenc_chan, &stream_packet);
		return -1;
	}

	p_carrier->data_len = stream_packet.u32Len + sizeof(enc_av_data_head);
	p_carrier->data_offset = 0;
	memcpy(p_carrier->data, &audio_data_head, sizeof(enc_av_data_head));
	memcpy(p_carrier->data+sizeof(enc_av_data_head), stream_packet.pStream, stream_packet.u32Len);

	qlist_push(&main_data.avout_queue, p_carrier);

	HI_MPI_AENC_ReleaseStream(aenc_chan, &stream_packet);	

	return 0;
}

void *enc_stream_handle(void *p_arg)
{
    HI_S32 max_fd = 0;
    struct timeval timeout;
    fd_set read_fds;
	fd_set save_fds;
    HI_S32 ret;
	int venc_fd;
	int aenc_fd;
    VENC_CHN venc_chan;
	AENC_CHN aenc_chan;
	stENC_CHLSET *p_chanSet;
	
	venc_chan = main_data.chan_mapnum;
	aenc_chan = main_data.chan_mapnum;
	p_chanSet = &g_encset_slv->enc_chls[main_data.chan_srcnum];

	printf("venc_chan %d\n", venc_chan);

	FD_ZERO(&read_fds);

    venc_fd = HI_MPI_VENC_GetFd(venc_chan);
    if (venc_fd < 0) 
	{
        enc_err("HI_MPI_VENC_GetFd failed with %#x!\n", venc_fd);
        return NULL;
    }

	FD_SET(venc_fd, &read_fds);
    if (venc_fd > max_fd)
    {
        max_fd = venc_fd;
    }

	/*aenc_fd = HI_MPI_AENC_GetFd(aenc_chan);
    if (aenc_fd < 0){
		enc_err("HI_MPI_VENC_GetFd failed with %#x!\n", venc_fd);
		return NULL;

    }
	
	FD_SET(aenc_fd, &read_fds);	
	if (aenc_fd > max_fd)
	{
		max_fd = aenc_fd;
	}*/
	save_fds = read_fds;

    while (p_chanSet->encchl_isrun)
    {
		read_fds = save_fds;
		
        timeout.tv_sec  = 5;
        timeout.tv_usec = 0;
        ret = select(max_fd + 1, &read_fds, NULL, NULL, &timeout);
        if (ret < 0)
        {
            enc_err("select failed!\n");
            break;
        }
        else if (ret == 0)
        {
            enc_err("get venc stream time out\n");
			sleep(1);
            continue;
        }

        if (FD_ISSET(venc_fd, &read_fds))
        {
			enc_video_stream(venc_chan, p_chanSet);
        }

		/*if (FD_ISSET(aenc_fd, &read_fds))
		{
			enc_audio_stream(aenc_chan, p_chanSet);
		}*/
    }

    return NULL;
}


